#include <sstream>

#ifdef SYPX_MEMCHECK
#include "mmgr.h"
#endif

#include "Level.h"
#include "Instances.h"
#include "Actor.h"
#include "Actors.h"

namespace SheepDefence
{
	Level::Level() : stateAge(0), score(0), dog(0), timer_1(1), timer_10(0.1f), timer_25(0.04f), numWolves(0), numSheep(0), stars(0)
	{
	}

	Level::~Level()
	{
		delete background;
		delete clock;
		delete briefBox;
		
		for (unsigned int i = 0; i < instances.size(); i++)
			delete instances[i];

		for (unsigned int i = 0; i < actors.size(); i++)
			delete actors[i];	

		delete environment;
	}

	Level::Level(SypX::Font* f, const SypX::String& filename, Breed choice) : stateAge(0), font(f), score(0), dog(0), timer_1(1), timer_10(0.1f), timer_25(0.04f), numWolves(0), numSheep(0), character(choice), stars(0)
	{
		clockMesh = new SypX::Mesh();
		clockMesh->initAsQuad(20.0f, 20.0f);

		clock = new SypX::MeshInstance();
		clock->useMesh(clockMesh);
		clock->setPosition(SypX::Vec3(260.0f, 440.0f, 0.9f));		
		clock->useTexture(SypX::Texture::loadFromFile("Textures/clock.png"));

		SypX::FileInputStream fin;
		SypX::String temp, bgName;
		int numActors;
		
		SypX::String path = SypX::Engine::getFilePath(filename);
		fin.open(path.c_str());		//load level file

		// Background image
		std::getline(fin, bgName);		//read in background filename
		
		backgroundMesh = new SypX::Mesh();
		backgroundMesh->initAsQuad(320, 480);

		background = new SypX::MeshInstance();
		background->useMesh(backgroundMesh);
		background->setPosition(SypX::Vec3(160.0f, 240.0f, 0));		
		SypX::ErrorLogger::getLogger() << "Loading background: \"" << bgName << "\"\n";
		background->useTexture(SypX::Texture::loadFromFile(bgName));

		// Read winning and losing conditions
		fin >> countdown;					//read in timer starting value
		fin >> wolfCount;
		fin >> win;
		fin >> sheepCount;
		fin >> lose;

		max = 0;
		min = 0;

		if ((win == 1 || lose == 1) && (wolfCount != -1))
			max += countdown;

		// Load Actors
		fin >> numActors;

		for (int i = 0; i < numActors; i++)
		{
			fin >> temp;					//read actor file name

			if (temp == "Actors/dog.act")
			{
				switch (character)
				{
				case TERRIER:
					temp = "Actors/terrier.act";
					break;
				case COLLIE:
					temp = "Actors/collie.act";
					break;
				case GREYHOUND:
					temp = "Actors/greyhound.act";
					break;
				}
			}

			Actor* currActor = Actor::loadActor(temp);
			actors.push_back(currActor);

			// Load Instances
			int numInstances;
			fin >> numInstances;

			for (int j = 0; j < numInstances; j++)
			{
				Instance* currInstance;

				switch (currActor->type) {
					case WOLF:
						currInstance = new WolfInstance(fin, static_cast<Wolf*>(currActor), this);
						wolves.push_back(static_cast<WolfInstance*>(currInstance));
						break;
					case DOG:
						currInstance = new DogInstance(fin, static_cast<Dog*>(currActor), this);
						assert(dog == NULL && "More than 1 dog in level file");
						dog = static_cast<DogInstance*>(currInstance);
						break;
					case SHEEP:
						currInstance = new SheepInstance(fin, static_cast<Sheep*>(currActor), this);
						sheep.push_back(static_cast<SheepInstance*>(currInstance));
						break;
					case OBSTACLE:
						currInstance = new ObstacleInstance(fin, static_cast<Obstacle*>(currActor), this);
						obstacles.push_back(static_cast<ObstacleInstance*>(currInstance));
						break;
				}

				instances.push_back(currInstance);
			}
		}

		SypX::ErrorLogger::getLogger() << "Min: " << min << ", Max: " << max << "\n";

		if (wolfCount != -1)
		{
			if (win == 0)	// no other way of winning
				min += (int) ((numWolves - wolfCount) * wolves[0]->actor->hp);	// use first wolf's hp

			max += (int) ((wolves.size() - wolfCount) * wolves[0]->actor->hp);
		}
		else
		{
			if (wolves.size() > 0)
				max += (int) (wolves.size() * wolves[0]->actor->hp);
		}

		SypX::ErrorLogger::getLogger() << "Min: " << min << ", Max: " << max << "\n";

		if (sheepCount != -1)
		{
			min += sheepCount + 1;
			max += (int) (numSheep * 2 * sheep[0]->actor->hp);	// use first sheep's hp
		}
		else 
		{
			if (sheep.size() > 0)
				max += (int) (numSheep * 2 * sheep[0]->actor->hp);
		}

		SypX::ErrorLogger::getLogger() << "Min: " << min << ", Max: " << max << "\n";

		int waypoints;
		fin >> waypoints;

		SypX::ErrorLogger::getLogger() << waypoints << " waypoints identified\n";
		environment = new Graph(waypoints, this);

		for (int i = 0; i < waypoints; i++)
		{
			float x, y;
			fin >> x;
			fin >> y;
			environment->nodes[i]->init(x, y, environment);

			int neighbours;
			fin >> neighbours;

			for (int j = 0; j < neighbours; j++)
			{
				int neighbour;
				fin >> neighbour;

				environment->nodes[i]->addNeighbour(environment->nodes[neighbour]);
			}
		}

		std::getline(fin, temp);	// absorb linebreak

		brief = "";
		while (std::getline(fin, temp) != NULL)
			brief = brief + temp + "\n"; // TODO: add \n when using textbox
		
		SypX::ErrorLogger::getLogger() << "Brief text: " << brief << "\n";
		briefBox =  new SypX::TextBox(font, 250.0f, 250.0f, 10);
		briefBox->setBgColor(SypX::Vec4(0, 0, 0, 1.0f));
		briefBox->setBorderColor(SypX::Vec4(1.0f, 1.0f, 1.0f, 1.0f));		
		briefBox->getMeshInstance()->setPosition(SypX::Vec3(160.0f, 240.0f, 1.1f));
		briefBox->setText("     Mission Briefing\n" + brief);	
		
		environment->update();

		state = BRIEFING;
		stateAge = 0;

		
	}

	void Level::updateLevelState()
	{
		switch(state)
		{
		case BRIEFING:
		case WIN:
		case GAMEOVER:
			break;

		case INPROGRESS:

			float temp = stateAge;

			if (win == 1 && stateAge >= countdown)
				changeState(WIN);
			
			if (lose == 1 && stateAge >= countdown)
				changeState(GAMEOVER);
			
			if (wolfCount != -1 && numWolves <= wolfCount)
				changeState(WIN);
			
			if (sheepCount != -1 && numSheep <= sheepCount)
				changeState(GAMEOVER);
			
			if (state == WIN)
			{
				std::vector<SheepInstance*>::iterator i = sheep.begin();
				while (i != sheep.end())
				{
					if ((*i)->state != DEAD)
					{
						if ((*i)->hp == (*i)->actor->hp)
							score += (int) (*i)->actor->hp;

						score += (int) (*i)->hp;
					}
					i++;
				}

				std::vector<WolfInstance*>::iterator j = wolves.begin();
				while (j != wolves.end())
				{
					if ((*j)->state == DEAD)
						score += (int) (*j)->actor->hp;
					j++;
				}

				if ((win == 1 || lose == 1) && (wolfCount != -1))
					score += (int) (countdown - temp);

				score = score < 0 ? 0 : score;

				stars = (int) (4 * (score - min)) / (max - min);
				stars = stars > 3 ? 3 : stars;
			}

			break;
		}
	}

	WolfInstance* Level::acquireTarget(float x, float y)
	{
		std::vector<WolfInstance*>::iterator i = wolves.begin();

		while (i != wolves.end()) 
		{
			if ((*i)->state != DEAD && ((*i)->x - x) * ((*i)->x - x) + ((*i)->y - y) * ((*i)->y - y) < (*i)->actor->collision * (*i)->actor->collision)
				return (*i);
			i++;
		}

		return NULL;
	}


	void Level::move(float x, float y)
	{
		if (state == BRIEFING && stateAge < 1)
			return;
		else if (state == BRIEFING)
		{
			changeState(INPROGRESS);
			return;
		}

		WolfInstance* potential_target = acquireTarget(x, y);

		dog->move(x, y, true);

		if (dog->state == POUNCING || dog->state == BITING)
		{
			if (potential_target != NULL && potential_target != dog->target)
				dog->next_target = potential_target;
		}
		else
		{
			if (potential_target != NULL)
			{
				SypX::ErrorLogger::getLogger() << "Tracking\n";
				dog->changeState(TRACKING);
				dog->target = potential_target;
				dog->targetSprite = potential_target->sprite;
			}
			else
			{
				dog->changeState(RUNNING);
				dog->targetSprite = 0;
				dog->target = 0;
			}
		}
	}

	void Level::act(float time)
	{
		if (state == BRIEFING)
			return;

		timer_1 += time;
		if (timer_1 > 1)
			timer_1 = 0;

		timer_10 += time;
		if (timer_10 > 0.1)
			timer_10 = 0;

		timer_25 += time;
		if (timer_25 > 0.04)
			timer_25 = 0;

		if (timer_1 == 0)
			updateLevelState();

		environment->setup(wolves, dog, sheep, obstacles, time);

		std::vector<SheepInstance*>::iterator i = sheep.begin();
		while (i != sheep.end())
		{
			(*i)->act(time);
			i++;
		}

		std::vector<WolfInstance*>::iterator j = wolves.begin();
		while (j != wolves.end())
		{
			(*j)->act(time);
			j++;
		}

		dog->act(time);

		std::vector<WolfInstance*>::iterator k = wolves.begin();
		while (k != wolves.end())
		{
			(*k)->respond();
			k++;
		}

		std::vector<SheepInstance*>::iterator m = sheep.begin();
		while (m != sheep.end())
		{
			(*m)->respond();
			m++;
		}
	}

	void Level::advance(float time)
	{
		stateAge += time;

		if (state == BRIEFING)
			return;

		std::vector<Instance*>::iterator i = instances.begin();
		while (i != instances.end())
		{
			(*i)->advance(time);
			i++;
		}
	}

	void Level::draw()
	{
		if (state == BRIEFING)	// TODO: use textbox
		{
			SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
			//font->drawText(brief, 110.0f, 240.0f, 1.0f);
			briefBox->draw();
			SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(0.5f, 0.5f, 0.5f, 1.0f));			
		}

		SypX::Engine::getSingleton()->getRenderer()->drawMesh(background);

		std::vector<Instance*>::iterator i = instances.begin();
		while (i != instances.end())
		{
			if((*i) == dog->target)
			{
				++i;
				continue;	//skip drawing the target, we have plans for him :)
			}

			(*i)->draw();
			i++;
		}

		SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(1.0f,1.0f,1.0f, 1.0f));
		std::vector<Actor*>::iterator j = actors.begin();
		while (j != actors.end())
		{
			(*j)->draw();
			j++;
		}

		if(dog->targetSprite && state == INPROGRESS)
		{//This is the plan
			SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(1.0f, 0, 0, 1.0f));
			dog->targetSprite->update();
			dog->targetSprite->getSprite()->drawBufferedInstances();
		}

		dog->drawStaminaBar();

		if (countdown > 0 && state == INPROGRESS)
		{
			std::ostringstream oss;
			oss << (int) (countdown - stateAge);
			font->drawText(oss.str(), 280.0f, 430.0f, 1.0f);
			if (countdown - stateAge < 10)
				SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(1.0f, 0, 0, 1.0f));
			else
				SypX::Engine::getSingleton()->getRenderer()->setActiveColor(SypX::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
			font->drawBufferedInstances();
			SypX::Engine::getSingleton()->getRenderer()->drawMesh(clock);
		}
	}

	void Level::changeState(LevelState newState)
	{
		if (state != newState)
		{
			state = newState;
			stateAge = 0;
		}
	}
}